package com.ccrfid.rmc6.service.business.patrol;

import com.ccrfid.rmc6.appconfig.exception.pojo.AppException;
import com.ccrfid.rmc6.appconfig.exception.pojo.CustomErrors;
import com.ccrfid.rmc6.entity.business.patrol.*;
import com.ccrfid.rmc6.entity.device.LocatorEntity;
import com.ccrfid.rmc6.entity.personnel.PersonnelMoveEntity;
import com.ccrfid.rmc6.entity.poi.PoiLocationEntity;
import com.ccrfid.rmc6.pojo.dto.patrol.BreakTimeRangeDto;
import com.ccrfid.rmc6.pojo.dto.patrol.PatrolCompletionRateDto;
import com.ccrfid.rmc6.pojo.dto.patrol.PatrolCompletionRateMonthlyDto;
import com.ccrfid.rmc6.repository.business.patrol.*;
import com.ccrfid.rmc6.repository.poi.PoiLocationRepository;
import com.ccrfid.rmc6.service.poi.PoiService;
import com.ccrfid.rmc6.util.DateUtil;
import com.ccrfid.rmc6.util.GeomUtil;
import com.vividsolutions.jts.geom.Point;
import org.hibernate.query.NativeQuery;
import org.hibernate.transform.Transformers;
import org.hibernate.type.DateType;
import org.hibernate.type.DoubleType;
import org.hibernate.type.IntegerType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import javax.ws.rs.core.Response;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.*;

@Service
@Transactional
public class PatrolServiceImpl implements PatrolService {
    private static final Logger logger = LoggerFactory.getLogger(PatrolServiceImpl.class);
    @PersistenceContext
    private EntityManager entityManager;
    @Autowired
    private PatrolScheduleRepository patrolScheduleRepository;
    @Autowired
    private PatrolLocationRecordRepository patrolLocationRecordRepository;
    @Autowired
    private PatrolLocationOverallRepository patrolLocationOverallRepository;
    @Autowired
    private PatrolGroupRepository patrolGroupRepository;
    @Autowired
    private PatrolGroupPersonnelRepository patrolGroupPersonnelRepository;
    @Autowired
    private PatrolTimeRangeRepository patrolTimeRangeRepository;
    @Autowired
    private PatrolLocationRepository patrolLocationRepository;
    @Autowired
    private PoiLocationRepository poiLocationRepository;
    @Autowired
    private PoiService poiService;

    @Value("${patrol.time.interval}")
    private Integer patrolTimeInterval;

    @Override
    public List<PatrolScheduleEntity> getPatrolScheduleList(Date startDate, Date stopDate) {
        return patrolScheduleRepository.getAllByDutyDateBetween(startDate, stopDate);
    }

    @Override
    public PatrolScheduleEntity getPatrolSchedule(Long scheduleId) throws AppException {
        PatrolScheduleEntity patrolScheduleEntity = patrolScheduleRepository.getById(scheduleId);
        if (patrolScheduleEntity == null) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.resource_not_found.getCode(), CustomErrors.resource_not_found.getReason(), "");
        } else {
            return patrolScheduleEntity;
        }
    }

    @Override
    public PatrolScheduleEntity getPatrolSchedule(Date dutyDate, Long timeRangeId) throws AppException {
        PatrolScheduleEntity patrolSchedule = patrolScheduleRepository.getByDutyDateAndTimeRangeId(dutyDate, timeRangeId);
        if (patrolSchedule == null) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.resource_not_found.getCode(), CustomErrors.resource_not_found.getReason(), "");
        } else {
            return patrolSchedule;
        }
    }

    @Override
    public PatrolScheduleEntity createPatrolSchedule(PatrolScheduleEntity patrolSchedule) throws AppException {
        if (patrolScheduleRepository.existsByDutyDateAndTimeRangeId(
                patrolSchedule.getDutyDate(), patrolSchedule.getTimeRangeId())) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.resource_conflict.getCode(), CustomErrors.resource_conflict.getReason(), "");
        }
        PatrolGroupEntity patrolGroupEntity = patrolGroupRepository.getById(patrolSchedule.getGroupId());
        if (patrolGroupEntity == null) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.patrol_group_not_found.getCode(), CustomErrors.patrol_group_not_found.getReason(), "");
        }
        if (!patrolTimeRangeRepository.existsById(patrolSchedule.getTimeRangeId())) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.patrol_time_range_not_found.getCode(), CustomErrors.patrol_time_range_not_found.getReason(), "");
        }
        patrolSchedule.setGroupName(patrolGroupEntity.getGroupName());
        patrolSchedule.setPatrolStatus(-1);     //初始化
        patrolSchedule.setCompletionRate(0.0);  //初始化
        return patrolScheduleRepository.save(patrolSchedule);
    }

    @Override
    public PatrolScheduleEntity updatePatrolSchedule(Long scheduleId, PatrolScheduleEntity patrolSchedule) throws AppException {
        if (!scheduleId.equals(patrolSchedule.getId())) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.different_args.getCode(), CustomErrors.different_args.getReason(), "");
        }
        if (!patrolTimeRangeRepository.existsById(patrolSchedule.getTimeRangeId())) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.patrol_time_range_not_found.getCode(), CustomErrors.patrol_time_range_not_found.getReason(), "");
        }
        PatrolGroupEntity patrolGroupEntity = patrolGroupRepository.getById(patrolSchedule.getGroupId());
        if (patrolGroupEntity == null) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.patrol_group_not_found.getCode(), CustomErrors.patrol_group_not_found.getReason(), "");
        }
        patrolSchedule.setGroupName(patrolGroupEntity.getGroupName());
        patrolSchedule.setPatrolStatus(-1);     //初始化
        patrolSchedule.setCompletionRate(0.0);  //初始化
        return patrolScheduleRepository.save(patrolSchedule);
    }

    @Override
    public Boolean deletePatrolSchedule(Long scheduleId) throws AppException {
        if (!patrolScheduleRepository.existsById(scheduleId)) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.resource_not_found.getCode(), CustomErrors.resource_not_found.getReason(), "");
        }
        patrolScheduleRepository.deleteById(scheduleId);
        return true;
    }

    @Override
    public List<PatrolLocationOverallEntity> getPatrolScheduleLocationOverall(Long scheduleId) {
        List<PatrolLocationOverallEntity> list = patrolLocationOverallRepository.getAllByScheduleId(scheduleId);
        return list;
    }

    @Override
    public List<PatrolLocationRecordEntity> getPatrolScheduleLocationRecordList(Long scheduleId, Long locationId) {
        List<PatrolLocationRecordEntity> list = patrolLocationRecordRepository.getAllByScheduleIdAndLocationId(scheduleId, locationId);
        return list;
    }

    @Override
    public List<PatrolCompletionRateDto> getPatrolAverageCompletionRate(Date startDate, Date stopDate) {
        String sql = "select duty_date as dutyDate, avg(completion_rate) as completionRate " +
                " from patrol_schedule " +
                " where duty_date between :startD and :stopD and patrol_status <> -1 " +
                " group by duty_date";
        List<PatrolCompletionRateDto> list = entityManager.createNativeQuery(sql)
                .setParameter("startD", startDate)
                .setParameter("stopD", stopDate)
                .unwrap(NativeQuery.class)
                .addScalar("dutyDate", DateType.INSTANCE)
                .addScalar("completionRate", DoubleType.INSTANCE)
                .setResultTransformer(Transformers.aliasToBean(PatrolCompletionRateDto.class))
                .list();
        return list;
    }

    @Override
    public List<PatrolCompletionRateMonthlyDto> getPatrolAverageCompletionRateMonthly(Date startDate, Date stopDate) {
        String sql = "select year(duty_date) as 'year', month(duty_date) as 'month', avg(completion_rate) as completionRate " +
                " from patrol_schedule " +
                " where duty_date between :startD and :stopD and patrol_status <> -1 " +
                " group by year(duty_date), month(duty_date)";
        List<PatrolCompletionRateMonthlyDto> list = entityManager.createNativeQuery(sql)
                .setParameter("startD", startDate)
                .setParameter("stopD", stopDate)
                .unwrap(NativeQuery.class)
                .addScalar("year", IntegerType.INSTANCE)
                .addScalar("month", IntegerType.INSTANCE)
                .addScalar("completionRate", DoubleType.INSTANCE)
                .setResultTransformer(Transformers.aliasToBean(PatrolCompletionRateMonthlyDto.class))
                .list();
        return list;
    }

    @Override
    @Scheduled(cron = "0 15 1 * * ?")
    //Scheduled(cron = "秒 分 时 日 月 星期")
    //定时任务，每天凌晨1点15分执行一次
    public void doStatisticPatrolSchedules() {
        Date today = new Date(new java.util.Date().getTime());
        List<PatrolScheduleEntity> patrolScheduleList =
                patrolScheduleRepository.getAllByPatrolStatusAndDutyDateLessThan(-1, today);
        List<PatrolLocationEntity> patrolLocationList = patrolLocationRepository.getAllBy();
        if (patrolScheduleList != null && patrolLocationList != null && patrolLocationList.size() > 0) {
            for (PatrolScheduleEntity patrolScheduleEntity : patrolScheduleList) {
                //删除结果
                patrolLocationRecordRepository.deleteAllByScheduleId(patrolScheduleEntity.getId());
                patrolLocationOverallRepository.deleteAllByScheduleId(patrolScheduleEntity.getId());
                //重新统计
                doStatistic(patrolScheduleEntity, patrolLocationList);
            }
        }
    }

    @Override
    public void redoStatisticPatrolSchedules(Date startDate, Date stopDate) {
        List<PatrolScheduleEntity> patrolScheduleList = null;
        Date yesterday = new Date(DateUtil.getOneDayBefore(new java.util.Date()).getTime());
        if (stopDate.after(yesterday)) {
            patrolScheduleList = patrolScheduleRepository.getAllByDutyDateBetween(startDate, yesterday);
        } else {
            patrolScheduleList = patrolScheduleRepository.getAllByDutyDateBetween(startDate, stopDate);
        }
        List<PatrolLocationEntity> patrolLocationList = patrolLocationRepository.getAllBy();
        if (patrolScheduleList != null && patrolLocationList != null && patrolLocationList.size() > 0) {
            for (PatrolScheduleEntity patrolScheduleEntity : patrolScheduleList) {
                //初始化
                patrolScheduleEntity.setPatrolStatus(-1);
                patrolScheduleEntity.setCompletionRate(0.0);
                //删除结果
                patrolLocationRecordRepository.deleteAllByScheduleId(patrolScheduleEntity.getId());
                patrolLocationOverallRepository.deleteAllByScheduleId(patrolScheduleEntity.getId());
                //重新统计
                doStatistic(patrolScheduleEntity, patrolLocationList);
            }
        }
    }

    private void doStatistic(PatrolScheduleEntity patrolScheduleEntity, List<PatrolLocationEntity> patrolLocationList) {
        PatrolTimeRangeEntity timeRangeEntity = patrolTimeRangeRepository.getById(patrolScheduleEntity.getTimeRangeId());
        Timestamp startTime = new Timestamp(
                DateUtil.mergeDateAndTime(patrolScheduleEntity.getDutyDate(), timeRangeEntity.getStartTime())
                        .getTime());
        Timestamp stopTime = new Timestamp(
                DateUtil.mergeDateAndTime(patrolScheduleEntity.getDutyDate(), timeRangeEntity.getStopTime())
                        .getTime());
        List<PatrolGroupPersonnelEntity> personnelList =
                patrolGroupPersonnelRepository.getAllByGroupId(patrolScheduleEntity.getGroupId());

        List<BreakTimeRangeDto> breakTimeList = getBreakTimeList(patrolScheduleEntity.getDutyDate(), timeRangeEntity);

        List<PatrolLocationOverallEntity> overallList = new ArrayList<>();
        for (PatrolLocationEntity patrolLocation : patrolLocationList) {
            List<PatrolLocationRecordEntity> list = getAllPatrolPersonnelLocationRecords(
                    patrolScheduleEntity.getId(), patrolLocation, getLocators(patrolLocation.getLocationId()),
                    personnelList, startTime, stopTime);
            //analysisRecords
            PatrolLocationOverallEntity locationOverall = analysisRecords(list, startTime, stopTime, breakTimeList,
                    patrolScheduleEntity.getId(), patrolLocation);
            overallList.add(locationOverall);
            //保存record记录
            savePatrolLocationRecordList(list);
        }
        //保存overall记录
        savePatrolLocationOverallList(overallList);
        //保存patrolSchedule
        savePatrolSchedule(patrolScheduleEntity, overallList);
    }

    private PatrolLocationOverallEntity analysisRecords(List<PatrolLocationRecordEntity> recordList,
                                                        Timestamp startTime, Timestamp stopTime,
                                                        List<BreakTimeRangeDto> breakTimeList,
                                                        Long scheduleId, PatrolLocationEntity patrolLocation) {
        PatrolLocationOverallEntity overallEntity = new PatrolLocationOverallEntity(scheduleId,
                patrolLocation.getLocationId(), patrolLocation.getLocationName());
        if (recordList != null && recordList.size() > 0) {
            //first
            PatrolLocationRecordEntity firstRecord = recordList.get(0);
            if (moreThanXXMin(patrolTimeInterval, startTime, firstRecord.getEnterTime())) {
                firstRecord.setPatrolStatus(1);
            }
            for (int i = 0; i < recordList.size() - 1; i++) {
                if (moreThanXXMin(patrolTimeInterval, recordList.get(i).getEnterTime(), recordList.get(i + 1).getEnterTime())) {
                    if (!nearBreakTime(recordList.get(i).getEnterTime(), breakTimeList)) {
                        recordList.get(i).setPatrolStatus(1);
                    }
                    if (!nearBreakTime(recordList.get(i + 1).getEnterTime(), breakTimeList)) {
                        recordList.get(i + 1).setPatrolStatus(1);
                    }
                }
            }
            //last
            PatrolLocationRecordEntity lastRecord = recordList.get(recordList.size() - 1);
            if (moreThanXXMin(patrolTimeInterval, stopTime, lastRecord.getEnterTime())) {
                lastRecord.setPatrolStatus(1);
            }

            //统计区域总体结果
            int totalCount = getPatrolTotalCount(startTime, stopTime, breakTimeList);
            int abnormalCount = 0;
            for (PatrolLocationRecordEntity recordEntity : recordList) {
                Integer patrolStatus = recordEntity.getPatrolStatus();
                if (recordEntity.getStayTime() < patrolLocation.getMinPatrolTime()) {
                    patrolStatus += 2;
                    recordEntity.setPatrolStatus(patrolStatus);
                }
                if (recordEntity.getPatrolStatus() > 0) {
                    abnormalCount++;
                }
            }
            if (abnormalCount > 0) {
                overallEntity.setPatrolStatus(1);
                int actualCount = recordList.size();
                double completionRate =
                        (double) (actualCount - abnormalCount) / (totalCount > actualCount ? totalCount : actualCount);
                overallEntity.setCompletionRate(completionRate);
            } else {
                overallEntity.setPatrolStatus(0);
                overallEntity.setCompletionRate(1.0);
            }
        } else {
            overallEntity.setPatrolStatus(2);   //缺勤，无人巡控
            overallEntity.setCompletionRate(0.0);
        }
        return overallEntity;
    }

    private int getPatrolTotalCount(Timestamp startTime, Timestamp stopTime, List<BreakTimeRangeDto> breakTimeList) {
        long workMinutes = (stopTime.getTime() - startTime.getTime()) / (60 * 1000);
        long breakMinutes = 0;
        if (breakTimeList != null && breakTimeList.size() > 0) {
            for (BreakTimeRangeDto breakTime : breakTimeList) {
                breakMinutes += (breakTime.getStopTime().getTime() - breakTime.getStartTime().getTime()) / (60 * 1000);
            }
        }
        int count = (int) (workMinutes - breakMinutes) / 20;
        return count;
    }

    private boolean nearBreakTime(Timestamp time, List<BreakTimeRangeDto> breakTimeList) {
        for (BreakTimeRangeDto breakTime : breakTimeList) {
            if (!moreThanXXMin(patrolTimeInterval, time, breakTime.getStartTime())
                    || !moreThanXXMin(patrolTimeInterval, time, breakTime.getStopTime())) {
                return true;
            }
        }
        return false;
    }

    private boolean moreThanXXMin(int XX, Timestamp t1, Timestamp t2) {
        if (t1.before(t2)) {
            long millis = t2.getTime() - t1.getTime();
            return millis > XX * 60 * 1000;
        } else {
            long millis = t1.getTime() - t2.getTime();
            return millis > XX * 60 * 1000;
        }
    }

    private List<BreakTimeRangeDto> getBreakTimeList(Date dutyDate, PatrolTimeRangeEntity workTimeRange) {
        List<BreakTimeRangeDto> list = new ArrayList<>();
        List<PatrolTimeRangeEntity> breakTimeRangeList = patrolTimeRangeRepository.getAllByTimeRangeType(2);
        for (PatrolTimeRangeEntity breakTimeRange : breakTimeRangeList) {
            if (breakTimeRange.getStartTime().after(workTimeRange.getStartTime())
                    && breakTimeRange.getStopTime().before(workTimeRange.getStopTime())) {
                Timestamp startTime = new Timestamp(
                        DateUtil.mergeDateAndTime(dutyDate, breakTimeRange.getStartTime())
                                .getTime());
                Timestamp stopTime = new Timestamp(
                        DateUtil.mergeDateAndTime(dutyDate, breakTimeRange.getStopTime())
                                .getTime());
                list.add(new BreakTimeRangeDto(startTime, stopTime));
            }
        }
        return list;
    }

    private Set<Integer> getLocators(Long locationId) {
        Set<Integer> locatorSet = new HashSet<>();
        PoiLocationEntity poiLocationEntity = poiLocationRepository.getById(locationId);
        if (poiLocationEntity != null) {
            List<LocatorEntity> list = poiService.getLocatorsInLocation(poiLocationEntity);
            for (LocatorEntity locatorEntity : list) {
                locatorSet.add(locatorEntity.getLocatorAddress());
            }
        }
        return locatorSet;
    }

    private List<PatrolLocationRecordEntity> getAllPatrolPersonnelLocationRecords(
            Long scheduleId, PatrolLocationEntity locationEntity, Set<Integer> locators,
            List<PatrolGroupPersonnelEntity> personnelList, Timestamp startTime, Timestamp stopTime) {
        PoiLocationEntity poiLocationEntity = poiLocationRepository.getById(locationEntity.getLocationId());
        if (poiLocationEntity != null) {
            List<PatrolLocationRecordEntity> list = new ArrayList<>();
            for (PatrolGroupPersonnelEntity personnelEntity : personnelList) {
                list.addAll(getPatrolPersonnelLocationRecords(
                        scheduleId, poiLocationEntity, personnelEntity, startTime, stopTime));
            }
            //按时间先后排序
            list.sort(new Comparator<PatrolLocationRecordEntity>() {
                @Override
                public int compare(PatrolLocationRecordEntity o1, PatrolLocationRecordEntity o2) {
                    return (int) (o1.getEnterTime().getTime() - o2.getEnterTime().getTime());
                }
            });
            return list;
        } else {
            return new ArrayList<>();
        }
    }

    private boolean locationContainsPos(PoiLocationEntity locationEntity, Integer floorNumber, Point pos) {
        if (locationEntity.getFloorNumber().equals(floorNumber)
                && locationEntity.getLocationPolygon().contains(pos)) {
            return true;
        }
        return false;
    }

    private List<PatrolLocationRecordEntity> getPatrolPersonnelLocationRecords(
            Long scheduleId, PoiLocationEntity locationEntity, PatrolGroupPersonnelEntity personnelEntity,
            Timestamp startTime, Timestamp stopTime) {
        String sql = "select * from personnel_move " +
                " where personnel_id = :personnelId " +
                " and (record_time between :startT and :stopT) " +
                " and ((front_floor_number = :fn and st_contains(:pg, point(front_x, front_y))) " +
                " or (current_floor_number = :fn and st_contains(:pg, point(current_x, current_y))))" +
                " order by id";
        List<PersonnelMoveEntity> moveList = entityManager.createNativeQuery(sql, PersonnelMoveEntity.class)
                .setParameter("personnelId", personnelEntity.getPersonnelId())
                .setParameter("startT", startTime)
                .setParameter("stopT", stopTime)
                .setParameter("fn", locationEntity.getFloorNumber())
                .setParameter("pg", locationEntity.getLocationPolygon())
                .getResultList();
        List<PatrolLocationRecordEntity> list = new ArrayList<>();
        if (moveList != null && moveList.size() > 0) {
            for (PersonnelMoveEntity moveEntity : moveList) {
                Point frontPos = GeomUtil.INSTANCE.createPoint(moveEntity.getFrontX(), moveEntity.getFrontY());
                Point currentPos = GeomUtil.INSTANCE.createPoint(moveEntity.getCurrentX(), moveEntity.getCurrentY());
                //进入该区域
                if (!locationContainsPos(locationEntity, moveEntity.getFrontFloorNumber(), frontPos)
                        && locationContainsPos(locationEntity, moveEntity.getCurrentFloorNumber(), currentPos)) {
                    list.add(new PatrolLocationRecordEntity(
                            scheduleId,
                            locationEntity.getId(), locationEntity.getLocationName(),
                            personnelEntity.getPersonnelId(), personnelEntity.getPersonnelName(),
                            moveEntity.getRecordTime(), null));
                }
                //停留该区域，或离开该区域
                if (locationContainsPos(locationEntity, moveEntity.getFrontFloorNumber(), frontPos)) {
                    if (list.size() > 0) {
                        PatrolLocationRecordEntity recordEntity = list.get(list.size() - 1);
                        recordEntity.setLeaveTime(moveEntity.getRecordTime());
                    } else {
                        list.add(new PatrolLocationRecordEntity(
                                scheduleId,
                                locationEntity.getId(), locationEntity.getLocationName(),
                                personnelEntity.getPersonnelId(), personnelEntity.getPersonnelName(),
                                startTime, moveEntity.getRecordTime()));
                    }
                }
            }
        }
        if (list.size() > 0) {
            for (int i = 0; i < list.size(); i++) {
                PatrolLocationRecordEntity recordEntity = list.get(i);
                if (recordEntity.getLeaveTime() == null) {
                    if (i < list.size() - 1) {
                        recordEntity.setLeaveTime(list.get(i + 1).getEnterTime());
                    } else {
                        recordEntity.setLeaveTime(stopTime);
                    }
                }
            }
        }
        return list;
    }

    @Deprecated
    private List<PatrolLocationRecordEntity> getPatrolPersonnelLocationRecords(
            Long scheduleId, PatrolLocationEntity locationEntity, Set<Integer> locatorSet,
            PatrolGroupPersonnelEntity personnelEntity, Timestamp startTime, Timestamp stopTime) {
        if (locatorSet.size() == 0) {
            locatorSet.add(-1);
        }
        String sql = "select * from personnel_move " +
                " where personnel_id = :personnelId " +
                " and (record_time between :startT and :stopT) " +
                " and (front_locator_address in :locators or current_locator_address in :locators) " +
                " order by id";
        List<PersonnelMoveEntity> moveList = entityManager.createNativeQuery(sql, PersonnelMoveEntity.class)
                .setParameter("personnelId", personnelEntity.getPersonnelId())
                .setParameter("startT", startTime)
                .setParameter("stopT", stopTime)
                .setParameter("locators", locatorSet)
                .getResultList();

        List<PatrolLocationRecordEntity> list = new ArrayList<>();
        if (moveList != null && moveList.size() > 0) {
            for (PersonnelMoveEntity moveEntity : moveList) {
                //进入该区域
                if (!locatorSet.contains(moveEntity.getFrontLocatorAddress())
                        && locatorSet.contains(moveEntity.getCurrentLocatorAddress())) {
                    list.add(new PatrolLocationRecordEntity(
                            scheduleId,
                            locationEntity.getLocationId(), locationEntity.getLocationName(),
                            personnelEntity.getPersonnelId(), personnelEntity.getPersonnelName(),
                            moveEntity.getRecordTime(), null));
                }
                //停留该区域，或离开该区域
                if (locatorSet.contains(moveEntity.getFrontLocatorAddress())) {
                    if (list.size() > 0) {
                        PatrolLocationRecordEntity recordEntity = list.get(list.size() - 1);
                        recordEntity.setLeaveTime(moveEntity.getRecordTime());
                    } else {
                        list.add(new PatrolLocationRecordEntity(
                                scheduleId,
                                locationEntity.getLocationId(), locationEntity.getLocationName(),
                                personnelEntity.getPersonnelId(), personnelEntity.getPersonnelName(),
                                startTime, moveEntity.getRecordTime()));
                    }
                }
            }
        }
        if (list.size() > 0) {
            for (int i = 0; i < list.size(); i++) {
                PatrolLocationRecordEntity recordEntity = list.get(i);
                if (recordEntity.getLeaveTime() == null) {
                    if (i < list.size() - 1) {
                        recordEntity.setLeaveTime(list.get(i + 1).getEnterTime());
                    } else {
                        recordEntity.setLeaveTime(stopTime);
                    }
                }
            }
        }
        return list;
    }

    private void savePatrolLocationRecordList(List<PatrolLocationRecordEntity> list) {
        for (PatrolLocationRecordEntity entity : list) {
            patrolLocationRecordRepository.save(entity);
        }
    }

    private void savePatrolLocationOverallList(List<PatrolLocationOverallEntity> list) {
        for (PatrolLocationOverallEntity entity : list) {
            patrolLocationOverallRepository.save(entity);
        }
    }

    private void savePatrolSchedule(PatrolScheduleEntity patrolSchedule, List<PatrolLocationOverallEntity> overallList) {
        if (overallList.size() > 0) {
            double completionRate = 0.0;
            int countAbnormal = 0;  //异常个数
            int countNoPatrol = 0;  //缺勤累计
            for (PatrolLocationOverallEntity overallEntity : overallList) {
                if (!overallEntity.getPatrolStatus().equals(0)) {
                    countAbnormal++;
                }
                completionRate += overallEntity.getCompletionRate();
                if (overallEntity.getPatrolStatus().equals(2)) {
                    countNoPatrol++;
                }
            }
            if (countNoPatrol == overallList.size()) {
                patrolSchedule.setPatrolStatus(2);
            } else {
                if (countAbnormal > 0) {
                    patrolSchedule.setPatrolStatus(1);
                } else {
                    patrolSchedule.setPatrolStatus(0);
                }
            }
            patrolSchedule.setCompletionRate(completionRate / overallList.size());  //平均值
            patrolScheduleRepository.save(patrolSchedule);
        }
    }
}
